const addKey = (state, idKey) => {
  const isObject = state instanceof Object;
  if (isObject && !state[idKey]) {
    return { ...state, [idKey]: Symbol() };
  } else {
    return state;
  }
};

const Reducers = {
  add: (state = [], { payload, idKey }) => {
    if (!Array.isArray(state)) return state;
    return [...state, addKey(payload, idKey)];
  },
  addBatch: (state = [], { payload = [], idKey }) => {
    if (!Array.isArray(state)) return state;
    const dataList = payload.map(p => {
      return addKey(p, idKey);
    });
    return [...state, ...dataList];
  },
  move: (state = [], { index, payload, idKey }) => {
    if (!Array.isArray(state)) return state;
    return Reducers.insert(Reducers.del(state, { payload, idKey }), {
      index,
      payload,
      idKey,
    });
  },
  insert: (state = [], { index, payload, idKey }) => {
    if (!Array.isArray(state)) return state;
    const clone = [...state];
    clone.splice(index, 0, addKey(payload, idKey));
    return clone;
  },
  insertBatch: (state = [], { index, payload, idKey }) => {
    if (!Array.isArray(state)) return state;
    const dataList = payload.map(p => {
      return addKey(p, idKey);
    });
    const clone = [...state];
    clone.splice(index, 0, ...dataList);
    return [...clone];
  },
  del: (state = [], { payload, idKey }) => {
    if (!Array.isArray(state)) return state;
    if (payload[idKey]) {
      return state.filter(t => t[idKey] !== payload[idKey]);
    } else {
      return state.filter(t => t !== payload);
    }
  },
  delBatch: (state = [], { ids, idKey }) => {
    if (!Array.isArray(state)) return state;
    return state.filter(t => !ids.includes(t[idKey] || t));
  },
  update: (state = {}, { payload, idKey }) => {
    if (Array.isArray(state)) {
      state.forEach(t => {
        if (t[idKey] === payload[idKey]) {
          Object.assign(t, payload);
        }
      });
      return [...state];
    } else if (state instanceof Object) {
      return { ...state, ...payload };
    } else {
      return state;
    }
  },
  replace: (state = {}, { payload, idKey }) => {
    if (Array.isArray(payload)) {
      const dataList = payload.map(p => {
        return addKey(p, idKey);
      });
      return dataList;
    } else {
      return payload;
    }
  },
};

// action={
//   index:0, 数组下标
//   parentId:0, 上级主键
//   ids:[], 批量删除的主键集合
//   payload:{}||[], 有效对象|| 批量对象集合
//   idKey:'id', 主键字段名
// }

/**
 * 返回存储对象Reducer
 * @param obj 存储对象
 * @returns {function(*=, *): *}
 */
const updateObj = obj => (state, action) => {
  return Reducers.update(state, {
    ...action,
    payload: obj,
  });
};

/**
 * 返回数组Reducer
 * @param name 数组存储对象key名
 * @param idKey 数组主键key名
 * @param operate 对数组进行的操作方式
 * @returns {function(*=, *=): *}
 */
const updateArrA = (name, idKey, operate) => (state, action) => {
  const data = Reducers[operate](state[name], {
    idKey: idKey || `${name}Id`,
    ...action,
  });
  return updateObj({ [name]: data })(state, action);
};

/**
 * 返回数组Reducer
 * @param name 数组存储对象key名
 * @param idKey 数组主键key名
 * @param operate 对数组进行的操作方式
 * @returns {function(*=, *=): *}
 */
const updateArrB = (name, idKey, operate) => (state, action) => {
  // 用数组上级id作存储对象
  const data = updateArrA(
    [action.parentId],
    idKey,
    operate,
  )(state[name], action);
  return updateObj({ [name]: data })(state, action);
};

/**
 *
 * @param name 数据存储数组名
 * @param idKey 数组主键字段名
 * @returns {{}[]}
 */
export const createReducer = (name, idKey) => {
  const R = {};
  Object.keys(Reducers).forEach(operate => {
    R[`${name}/${operate}`] = updateArrA(name, idKey, operate);
  });
  return R;
};

export const createReducerGroupByParentKey = (name, idKey) => {
  const R = {};
  Object.keys(Reducers).forEach(operate => {
    R[`${name}/${operate}`] = updateArrB(name, idKey, operate);
  });
  return R;
};

export default Reducers;
